﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Runtime.Serialization;

namespace TheStar.Lib
{
    public class SortedLinkedList<TEntity> : ICollection<TEntity>, IEnumerable<TEntity>, ICollection, IEnumerable, ISerializable, IDeserializationCallback
    {
        #region Fields
        private IComparer comparer;
        private LinkedList<TEntity> innerList;
        #endregion

        #region Constructors
        public SortedLinkedList()
        { }
        public SortedLinkedList(IComparer<TEntity> comparer)
            : this(new NonTypedComparer(comparer))
        { }
        public SortedLinkedList(IComparer comparer)
            : this()
        {
            this.Comparer = comparer;
        }
        public SortedLinkedList(IEnumerable<TEntity> collection)
            : this()
        {
            foreach (TEntity item in collection)
            {
                this.Add(item);
            }
        }
        public SortedLinkedList(IEnumerable<TEntity> collection, IComparer<TEntity> comparer)
            : this(collection, new NonTypedComparer(comparer))
        { }
        public SortedLinkedList(IEnumerable<TEntity> collection, IComparer comparer)
            : this(collection)
        {
            this.Comparer = comparer;
        }
        #endregion

        #region Properties
        public IComparer Comparer
        {
            get
            {
                if (this.comparer == null)
                {
                    if (typeof(TEntity).GetInterfaces().Any(a => a.IsGenericTypeDefinition && a.GetGenericTypeDefinition().Equals(typeof(IComparable<>))))
                    {
                        this.comparer = new DefaultTypedComparer();
                    }
                    else if (typeof(TEntity).GetInterfaces().Any(a => a.Equals(typeof(IComparable))))
                    {
                        this.comparer = new DefaultComparer();
                    }
                    else
                    {
                        throw new InvalidOperationException("Unknown type of comparison to be done to the type");
                    }
                }
                return this.comparer;
            }
            private set
            {
                if (value == null)
                {
                    throw new ArgumentNullException("comparer");
                }
                this.comparer = value;
            }
        }
        private LinkedList<TEntity> InnerList
        {
            get
            {
                if (this.innerList == null)
                {
                    this.innerList = new LinkedList<TEntity>();
                }
                return this.innerList;
            }
        }
        public LinkedListNode<TEntity> First { get { return this.InnerList.First; } }
        public LinkedListNode<TEntity> Last { get { return this.InnerList.Last; } }
        #endregion

        #region Methods
        private void AddSorted(LinkedListNode<TEntity> newNode)
        {
            LinkedListNode<TEntity> current = this.InnerList.First;
            if (current == null)
            {
                this.InnerList.AddFirst(newNode);
            }
            else
            {
                while (current != null && (this.Comparer.Compare(current.Value, newNode.Value) < 0))
                {
                    current = current.Next;
                }
                if (current == null)
                {
                    this.InnerList.AddLast(newNode);
                }
                else
                {
                    this.InnerList.AddBefore(current, newNode);
                }
            }
        }
        public void RemoveFirst()
        {
            this.InnerList.RemoveFirst();
        }
        public void RemoveLast()
        {
            this.InnerList.RemoveLast();
        }
        public LinkedListNode<TEntity> Find(TEntity value)
        {
            return this.InnerList.Find(value);
        }
        public LinkedListNode<TEntity> FindLast(TEntity value)
        {
            return this.InnerList.FindLast(value);
        }
        #endregion

        #region DefaultComparer Type
        private class DefaultTypedComparer : IComparer<TEntity>, IComparer
        {
            public DefaultTypedComparer()
            { }

            public int Compare(TEntity x, TEntity y)
            {
                return ((IComparable<TEntity>)x).CompareTo(y);
            }

            public int Compare(object x, object y)
            {
                return this.Compare((TEntity)x, (TEntity)y);
            }
        }
        private class DefaultComparer : IComparer
        {
            public int Compare(object x, object y)
            {
                return ((IComparable)x).CompareTo(y);
            }
        }
        private class NonTypedComparer : IComparer
        {
            private readonly IComparer<TEntity> comparer;

            public NonTypedComparer(IComparer<TEntity> comparer)
            {
                this.comparer = comparer;
            }

            public int Compare(object x, object y)
            {
                return this.comparer.Compare((TEntity)x, (TEntity)y);
            }
        }
        #endregion


        #region ICollection<TEntity> Members

        public void Add(TEntity item)
        {
            this.AddSorted(new LinkedListNode<TEntity>(item));
        }

        public void Clear()
        {
            this.InnerList.Clear();
        }

        public bool Contains(TEntity item)
        {
            return this.InnerList.Contains(item);
        }

        public void CopyTo(TEntity[] array, int arrayIndex)
        {
            this.InnerList.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return this.InnerList.Count; }
        }

        public bool IsReadOnly
        {
            get { return false; }
        }

        public bool Remove(TEntity item)
        {
            return this.InnerList.Remove(item);
        }
        #endregion

        #region IEnumerable<TEntity> Members

        public IEnumerator<TEntity> GetEnumerator()
        {
            return this.InnerList.GetEnumerator();
        }
        #endregion

        #region IEnumerable Members
        IEnumerator IEnumerable.GetEnumerator()
        {
            return ((IEnumerable)this.InnerList).GetEnumerator();
        }
        #endregion

        #region ISerializable Members
        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            this.InnerList.GetObjectData(info, context);
        }
        #endregion

        #region IDeserializationCallback Members
        public void OnDeserialization(object sender)
        {
            this.InnerList.OnDeserialization(sender);
        }
        #endregion

        #region ICollection Members
        void ICollection.CopyTo(Array array, int index)
        {
            ((ICollection)this.InnerList).CopyTo(array, index);
        }
        int ICollection.Count
        {
            get { return ((ICollection)this.InnerList).Count; }
        }
        bool ICollection.IsSynchronized
        {
            get { return ((ICollection)this.InnerList).IsSynchronized; }
        }
        object ICollection.SyncRoot
        {
            get { return ((ICollection)this.InnerList).SyncRoot; }
        }
        #endregion
    }
}
